/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "stdafx.h"
#include "caldav.h"
#include <strsafe.h>
#include <otlkcon_config.h>

#include <sstream>

#include <boost/regex/config.hpp>
#include <boost/regex.hpp>

CaldavConnection::CaldavConnection()
{
    //Default 5 minutes
    pollSecs = 300;

    loggedOn = FALSE;

    user = pass = uri = L"";
	InitializeCriticalSection(&objectCS);
//    pXMLHttpReq = NULL;
}

CaldavConnection::~CaldavConnection()
{
	DeleteCriticalSection(&objectCS);
}

HRESULT CaldavConnection::Init(wstring idStr)
{
    HRESULT result = S_OK;

    //CoInitialize(NULL);
//    result = CoCreateInstance(
//			    CLSID_XMLHTTPRequest,
//			    NULL,
//			    CLSCTX_INPROC_SERVER,
//			    IID_IXMLHttpRequest,
//			    (LPVOID*)&(pXMLHttpReq) );

    srand((unsigned int)time(NULL));

    // For uniquely identifying the connection
    // in the local system.
    id = idStr;

    return result;
}

HRESULT CaldavConnection::Logon(wstring u, wstring us, wstring p )
{
    HRESULT result = S_OK;
	OTLKCON_ENTERCRITICALSECTION(&objectCS, "HTTP>CaldavConnection::Logon");
	MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;

    if( u.size() < 8 )
    {
        // URL too short
        result = E_FAIL;
        return result;
    }

    // Normalize the URI
    int uriStart = (int)u.find_first_not_of(' ');
    int uriEnd   = (int)u.find_last_not_of(' ');
    u = u.substr(uriStart,uriEnd+1);

    if( u.at(u.size()-1) != '/' )
    {
        u.push_back('/');
    }

	int iAttempts = 2;

	while(iAttempts--)
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );
		
		result = pXMLHttpReq->open( L"OPTIONS",
			bstr_t(u.c_str()), 
			variant_t(VARIANT_TRUE), //asynch
			variant_t(us.c_str()), 
			variant_t(p.c_str()) );

		result = pXMLHttpReq->send();
		result = WaitForResult(pXMLHttpReq);
		if(FAILED(result))
		{
			//We need to abort request
			pXMLHttpReq = NULL;
			//pXMLHttpReq->abort();
		} // if
		else
		{
			break;
		} // else
	} // while
    
	//we can read some info
	CComBSTR resText;
	long status;
	
	if(SUCCEEDED(result))
	{
		result = pXMLHttpReq->getResponseHeader( L"DAV", &resText );
		OutputDebugStringW( resText );

		result = pXMLHttpReq->get_status( &status );
		if( status != 200 )
		{
			resText = (LPCWSTR)NULL;
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( (LPCWSTR)resText );
			return E_FAIL;
		}

		// Check that calendar-access is in the list
		if( wcsstr(resText, L"calendar-access") == NULL )
		{
			return E_FAIL;
		}
	} // if

	if(SUCCEEDED(result))
	{
		// Get some properties, make sure things are sane
		// on the server side.

		// RFC:
		// A calendar collection MUST report
		// the DAV:collection and CALDAV:calendar XML elements in the value of
		// the DAV:resourcetype property.
		pXMLHttpReq = NULL;
		iAttempts = 2;
		while(iAttempts--) 
		{
			result = CoCreateInstance(
				CLSID_XMLHTTPRequest,
				NULL,
				CLSCTX_INPROC_SERVER,
				IID_IXMLHttpRequest,
				(LPVOID*)&(pXMLHttpReq) );
			
			result = pXMLHttpReq->open( L"PROPFIND",
				bstr_t(u.c_str()), 
				variant_t(VARIANT_TRUE), 
				variant_t(us.c_str()), 
				variant_t(p.c_str()) );

			result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/xml" );
		    // result = pXMLHttpReq->setRequestHeader( L"Depth", L"1" );

			result = pXMLHttpReq->send( variant_t("<?xml version='1.0'?>\
												  <D:propfind xmlns:D=\"DAV:\"><D:allprop/></D:propfind>") );
			//<D:propfind xmlns:D=\"DAV:\"><D:prop><D:resourcetype/></D:prop></D:propfind>") );
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				pXMLHttpReq = NULL;
				//We need to abort request
				//pXMLHttpReq->abort();
			} // if
			else
			{
				break;
			} // else
		} // while
		if(SUCCEEDED(result))
		{
			result = pXMLHttpReq->get_status( &status );
			if( status != 200 && status != 207 )
			{
				resText = (LPCWSTR)NULL;
				result = pXMLHttpReq->get_statusText( &resText );
				OutputDebugStringW( (LPCWSTR)resText );
				return E_FAIL;
			}
			resText = (LPCWSTR)NULL;
			result = pXMLHttpReq->get_responseText( &resText );
//
//            resText = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
//<multistatus xmlns=\"DAV:\" xmlns:p=\"urn:ietf:params:xml:ns:caldav\">\
//  <response>\
//    <href>http://127.0.0.1:8080/ucaldav/user/caluser1/calendar/</href>\
//    <propstat>\
//      <prop>\
//        <displayname>calendar</displayname>\
//        <getlastmodified>20061105T023830Z</getlastmodified>\
//        <resourcetype>\
//          <collection/>\
//          <p:calendar/>\
//        </resourcetype>\
//        <getetag>\"20061105T023830Z-0\"</getetag>\
//        <p:max-resource-size>100000</p:max-resource-size>\
//      </prop>\
//      <status>HTTP/1.1 200 ok</status>\
//    </propstat>\
//  </response>\
//</multistatus>";

			OutputDebugStringW( (LPCWSTR)resText );

            // FIXME: NamespaceManager stuff doesn't seem to work

            //unsigned short *pre1, *pre2;

			//MSXML2::IMXNamespaceManagerPtr nsmgr;
			//result = nsmgr.CreateInstance( __uuidof(MSXML2::MXNamespaceManager40) );

			//pre1 = (unsigned short *)_wcsdup( L"D" );
			//pre2 = (unsigned short *)_wcsdup( L"DAV:" );
			//result = nsmgr->declarePrefix( pre1, pre2 );

			//pre1 = (unsigned short *)_wcsdup( L"C" );
            //pre2 = (unsigned short *)_wcsdup( L"urn:ietf:params:xml:ns:caldav" );
			//result = nsmgr->declarePrefix( pre1, pre2 );

			MSXML2::IXMLDOMDocument2Ptr responseXML;

            // For test alternate XML
            
            // result = responseXML.CreateInstance( __uuidof(MSXML2::DOMDocument) );
            // VARIANT_BOOL boolSuc;
            // responseXML->loadXML( resText, &boolSuc );

			result = pXMLHttpReq->get_responseXML( (IDispatch**)&responseXML );

            // FIXME: MSXML ignores default namespaces in XPath.

            result = responseXML->setProperty( bstr_t("SelectionNamespaces"), 
                variant_t("xmlns:D='DAV:' xmlns:C='urn:ietf:params:xml:ns:caldav'") );

			MSXML2::IXMLDOMNodePtr node, node2;
			// We need to find an ok status and calendar present here
			result = responseXML->selectSingleNode( 
                L"/D:multistatus/D:response/D:propstat/D:prop/D:resourcetype/C:calendar", &node );

			if( node )
			{
				result = node->selectSingleNode( L"../../../D:status", &node2);
				resText = (LPCWSTR)NULL;
				result = node2->get_text( &resText );

				if( wcsstr(resText, L" 200 ") == NULL )
				{
					return E_FAIL;
				}
			}
			else
			{
				// Some sought of error.  Do something
				DebugBreak();
			}

			// TODO: Check for the presence of D:collection

			user = us;
			pass = p;
			uri = u;

			// Just to keep track if Logon() has been called
			// at once.
			loggedOn = TRUE;
		} // if
	} // if

    return S_OK;
}

HRESULT CaldavConnection::PutNewComponent( icalcomponent *comp,
                                          wchar_t* lpszCalID,
                                          wchar_t** lpszCalEtag, ULONG *ulCalETag,
                                          ULONG ulFlags)
{
    errno_t errRes = 0;

    if(NULL == lpszCalEtag || NULL == lpszCalID 
        || 0 == wcslen(lpszCalID) || ulCalETag == 0)
    {
		return E_INVALIDARG;
    } // if

	HRESULT result = S_OK;
	wchar_t str[MAX_PATH+1] = {0};

	OTLKCON_ENTERCRITICALSECTION(&objectCS, "HTTP>CaldavConnection::PutNewComponent");
	MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;

	LPWSTR lpMatchHeader = L"If-None-Match";
	LPWSTR lpMatchHeaderValue = L"*";
	wchar_t szMatchHeaderValue[MAX_PATH+1] = {0};
	if(NULL != lpszCalID && 0 < wcslen(lpszCalID))
    {
//		if(NULL == *lpszCalEtag)
//		{
//			return E_INVALIDARG;
//		} // if

        result = ValidateFilename( wstring(lpszCalID) );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

		//TODO: modification existing
		errRes = _snwprintf_s((wchar_t *)&str, MAX_PATH+1, MAX_PATH,
			L"%s%s",
			uri.c_str(), lpszCalID);
//		lpMatchHeader = L"If-None-Match";
//		wcscpy(szMatchHeaderValue, *lpszCalEtag);
//		lpMatchHeaderValue = szMatchHeaderValue;
    } // if
	else
	{
		// Create a random ICS filename using the GUID format
		//We need to create ID while beginning upload message
		//Using this ID we will update Etag
		errRes = _snwprintf_s((wchar_t *)&str, MAX_PATH+1, MAX_PATH,
			L"%s/%0.2X%0.2X%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X.ics",
			uri.c_str(),
			rand()%256, rand()%256, rand()%256, rand()%256, rand()%256,
			rand()%256, rand()%256, rand()%256, rand()%256, rand()%256,
			rand()%256, rand()%256, rand()%256, rand()%256, rand()%256,
			rand()%256);
	} // else

    int iAttempts = 2;

	while(iAttempts--) 
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );

		result = pXMLHttpReq->open( L"PUT",
			bstr_t(str), 
			variant_t(VARIANT_TRUE),
			variant_t(user.c_str()), 
			variant_t(pass.c_str()) );

		result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/calendar" );

        if( !(ulFlags & OTLKCON_ACTION_MOD) )
        {
            // This is an ADD operation, hence check for existance
		    result = pXMLHttpReq->setRequestHeader( lpMatchHeader, lpMatchHeaderValue );
        }

		result = pXMLHttpReq->send( variant_t( icalcomponent_as_ical_string(comp) ) );
		if(SUCCEEDED(result))
		{
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				pXMLHttpReq = NULL;
				//pXMLHttpReq->abort();
			} // if
			else
			{
				break;
			} // else
		} // if
	} // while
	
	if(SUCCEEDED(result))
	{
		LONG status;
		CComBSTR resText = (LPCWSTR)NULL;

		result = pXMLHttpReq->get_responseText( &resText );
		OutputDebugStringW( (LPCWSTR)resText );

		result = pXMLHttpReq->get_status( &status );
		if( status != 200 && status != 201 && status != 204 && status != 207)
		{
			resText = (LPCWSTR)NULL;
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( (LPCWSTR)resText );
			return E_FAIL;
		}
		
		resText = (LPCWSTR)NULL;
		result = pXMLHttpReq->getResponseHeader( L"ETag", &resText );
		if(SUCCEEDED(result) && 0 < wcslen(resText))
		{
            *ulCalETag = (ULONG)wcslen(resText)+1;
			*lpszCalEtag = (wchar_t*)calloc(1, (*ulCalETag)*sizeof(wchar_t));
			errRes = wcscpy_s(*lpszCalEtag, (*ulCalETag), resText);

			result = S_OK;
		} // if
		//Save the ETag, etc.
		//Another message to update ETag ?
	} // if
    return result;
}

wstring CaldavConnection::ResourcenameFromUrl( wstring url )
{
    wstring res;

    basic_string <wchar_t>::size_type i = url.rfind( L"/" );
    res = url.substr( i+1 );

    return res;
}

int CaldavConnection::StatusString2Int( wstring stat )
{
    basic_string <wchar_t>::size_type i = stat.find( L" " );
    stat = stat.substr( i+1 );
    basic_string <wchar_t>::size_type j = stat.find( L" " );
    stat = stat.substr( 0, stat.length() - j );

    int res = _wtoi( stat.c_str() );

    return res;
}

HRESULT CaldavConnection::ReportResponseXML2Components( MSXML2::IXMLDOMDocument2Ptr response, 
                                     otlkcon_calendar_component_array **comps)
{
    HRESULT result = S_OK;
    CComBSTR resText = (LPCWSTR)NULL;
    otlkcon_calendar_component *tempComp = NULL;
    vector<otlkcon_calendar_component*> compsList;
    wstring tempName;
    ULONG dataSize = 0;
	//unsigned short *pre1, *pre2;
    errno_t resErr;

    // FIXME:  Doesn't work
    //MSXML2::IMXNamespaceManagerPtr nsmgr;
    //result = nsmgr.CreateInstance( __uuidof(MSXML2::MXNamespaceManager40) );

	//pre1 = (unsigned short *)_wcsdup( L"D" );
	//pre2 = (unsigned short *)_wcsdup( L"DAV:" );
	//result = nsmgr->declarePrefix( pre1, pre2 );
	//pre1 = (unsigned short *)_wcsdup( L"C" );
	//pre2 = (unsigned short *)_wcsdup( L"urn:ietf:params:xml:ns:caldav" );
	//result = nsmgr->declarePrefix( pre1, pre2 );

    result = response->setProperty( bstr_t("SelectionNamespaces"), 
                variant_t("xmlns:D='DAV:' xmlns:C='urn:ietf:params:xml:ns:caldav'") );

    MSXML2::IXMLDOMNodePtr node, node2, node3;
    MSXML2::IXMLDOMNodeListPtr nodeList = NULL;

    // Process resources.
    result = response->selectNodes( L"/D:multistatus/D:response", &nodeList );
    while( nodeList->nextNode( &node ) == S_OK )
    {   
        tempComp = (otlkcon_calendar_component *)calloc( 1,
                                        sizeof(otlkcon_calendar_component) );

        result = node->selectSingleNode( L"./D:propstat", &node2);
        if( result == S_OK )
        {
            // Get ETag
            result = node2->selectSingleNode( L"./D:prop/D:getetag", &node3);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                resText = (LPCWSTR)NULL;
                result = node2->get_text( &resText );
				OutputDebugStringW( (LPCWSTR)resText );

                // FIXME: Memory leak.  All continues should free memory
                free(tempComp);
                continue;
            }

			resText = (LPCWSTR)NULL;
            result = node3->get_text( &resText );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
            resErr = wcscpy_s(tempComp->hash, MAX_PATH, resText);

            // Get Status
            result = node2->selectSingleNode( L"./D:status", &node3);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
			resText = (LPCWSTR)NULL;
            result = node3->get_text( &resText );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
            tempComp->status = StatusString2Int( (LPCWSTR)resText );
            if( tempComp->status < 200 || tempComp->status > 299 )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }

            // Get displayname
            result = node2->selectSingleNode( L"./D:prop/D:displayname", &node3);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
			resText = (LPCWSTR)NULL;
            result = node3->get_text( &resText );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
            resErr = wcscpy_s(tempComp->name, MAX_PATH, resText);

            // Calendar data
            result = node2->selectSingleNode( L"./D:prop/C:calendar-data", &node3);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
			resText = (LPCWSTR)NULL;
            result = node3->get_text( &resText );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                free(tempComp);
                continue;
            }
            //tempComp->comp = icalparser_parse_string( ((char*)_bstr_t(resText)) );
            tempComp->data.pBytes = (LPBYTE)_wcsdup( resText );
            //wcscpy((wchar_t*)&(tempComp->data.bytes[0]), resText );
            tempComp->dataSize = (ULONG)(wcslen(resText)+1)*sizeof(wchar_t);
        }
        else
        {
            // Error maybe?
            result = node->selectSingleNode( L"./D:href", &node2);
            if( result == S_OK )
            {
				resText = (LPCWSTR)NULL;
                result = node2->get_text( &resText );
                tempName = resText;
				OutputDebugStringW( (LPCWSTR)resText );
                resErr = wcscpy_s(tempComp->name, MAX_PATH,
                        ResourcenameFromUrl( tempName ).c_str());

                // multiget error probably
                result = node->selectSingleNode( L"./D:status", &node2);
				resText = (LPCWSTR)NULL;
                result = node2->get_text( &resText );
				OutputDebugStringW( (LPCWSTR)resText );
                tempComp->status = StatusString2Int( (LPCWSTR)resText );
            }
			//So far skip that
			continue;
        }

        compsList.push_back(tempComp);
        dataSize += tempComp->dataSize + sizeof(otlkcon_calendar_component);
    }
  
    (*comps) = (otlkcon_calendar_component_array *)calloc(1, 
                    sizeof(otlkcon_calendar_component_array) + dataSize);
    (*comps)->count = (ULONG)compsList.size();
    (*comps)->size  = dataSize;

    // Flatten the list
    LPBYTE tempByte = &((*comps)->bytes[0]);
    for(ULONG i=0; i<compsList.size(); i++)
    {
        tempComp = compsList[i];
        memcpy(tempByte, tempComp,
                        sizeof(otlkcon_calendar_component));//+tempComp->dataSize 
        //FIXME: This should be a choice, decided by the union member used earlier
        memcpy(&(((otlkcon_calendar_component *)tempByte)->data.bytes[0]),
                                tempComp->data.pBytes, 
                                tempComp->dataSize);
        free(tempComp->data.pBytes);
        tempByte += sizeof(otlkcon_calendar_component)+tempComp->dataSize; 
        free(tempComp);
    }

    return result;
}

HRESULT CaldavConnection::GetCollectionLock( )
{
    HRESULT result = S_OK;

    return result;
}

HRESULT CaldavConnection::ReleaseCollectionLock( )
{
    HRESULT result = S_OK;

    return result;
}

HRESULT CaldavConnection::GetAllEventNames( map<wstring,wstring> *namePairs )
{
    HRESULT result = S_OK;

	OTLKCON_ENTERCRITICALSECTION(&objectCS, "HTTP>CaldavConnection::GetAllEventNames");
	MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;

    // Get all the resource names and ETags in the collection
    LONG status;
    CComBSTR resText = (LPCWSTR)NULL;
    char reqstr[1023] = {0};
	int  iAttempts = 2;
	wstring reqStr;
	// unsigned short *pre1, *pre2;

    while(iAttempts--)
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );

		result = pXMLHttpReq->open( L"REPORT",
			bstr_t(uri.c_str()), 
			variant_t(VARIANT_TRUE), 
			variant_t(user.c_str()), 
			variant_t(pass.c_str()) );

		result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/xml" );

		result = pXMLHttpReq->setRequestHeader( L"Depth", L"1" );

		// wstring reqStr = L"<?xml version=\"1.0\"?>\
		//<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\
		//  <D:prop xmlns:D=\"DAV:\">\
		//    <D:getetag/>\
		//    <D:displayname/>\
		//  </D:prop>\
		//  <C:filter>\
		//    <C:comp-filter name=\"VCALENDAR\">\
		//      <C:comp-filter name=\"VEVENT\">\
		//         <C:time-range start=\"";
		//  reqStr += OTLKCON_MIN_DATE_STR;
		//  reqStr += L"\" end=\"";
		//  reqStr += OTLKCON_MAX_DATE_STR;
		//  reqStr += L"\"/>\
		//      </C:comp-filter>\
		//    </C:comp-filter>\
		//  </C:filter>\
		//</C:calendar-query>";

		//FIXME: We need to be a little more sophisticated
		//      in retrieving the events.
		reqStr = L"<?xml version=\"1.0\"?>\
						  <C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\
						  <D:prop xmlns:D=\"DAV:\">\
						  <D:getetag/>\
						  <D:displayname/>\
						  </D:prop>\
						  <C:filter>\
						  <C:comp-filter name=\"VCALENDAR\">\
						  <C:comp-filter name=\"VEVENT\"/>\
						  </C:comp-filter>\
						  </C:filter>\
						  </C:calendar-query>";
		result = pXMLHttpReq->send( variant_t( reqStr.c_str() ) );

		if(SUCCEEDED(result))
		{
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				pXMLHttpReq = NULL;
				//pXMLHttpReq->abort();
			} // if
		} // if
		if(SUCCEEDED(result))
		{
			break;
		} // if
	}

	if(SUCCEEDED(result))
	{
		result = pXMLHttpReq->get_status( &status );
		if( status != 200 && status != 207 )
		{
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( reqStr.c_str() );
			OutputDebugStringW( (LPCWSTR)resText );
			resText = (LPCWSTR)NULL;
			result = pXMLHttpReq->get_responseText( &resText );
			OutputDebugStringW( (LPCWSTR)resText );
			return E_FAIL;
		}

		resText = (LPCWSTR)NULL;
		result = pXMLHttpReq->get_responseText( &resText );
		OutputDebugStringW( (LPCWSTR)resText );

		MSXML2::IXMLDOMDocument2Ptr responseXML;
		result = pXMLHttpReq->get_responseXML( (IDispatch**)&responseXML );

        // FIXME: This doesn't work

		//MSXML2::IMXNamespaceManagerPtr nsmgr;
		//result = nsmgr.CreateInstance( __uuidof(MSXML2::MXNamespaceManager40) );
		//pre1 = (unsigned short *)_wcsdup( L"D" );
		//pre2 = (unsigned short *)_wcsdup( L"DAV:" );
		//result = nsmgr->declarePrefix( pre1, pre2 );
		//pre1 = (unsigned short *)_wcsdup( L"C" );
		//pre2 = (unsigned short *)_wcsdup( L"urn:ietf:params:xml:ns:caldav" );
		//result = nsmgr->declarePrefix( pre1, pre2 );

        result = responseXML->setProperty( bstr_t("SelectionNamespaces"), 
                variant_t("xmlns:D='DAV:' xmlns:C='urn:ietf:params:xml:ns:caldav'") );

		MSXML2::IXMLDOMNodePtr node, node2, node3;
		MSXML2::IXMLDOMNodeListPtr nodeList = NULL;

		wstring tempEtag, tempName;

		result = responseXML->selectNodes( L"/D:multistatus/D:response/D:propstat/D:prop", &nodeList );
		while( nodeList->nextNode( &node ) == S_OK )
		{
			// Get ETag
			result = node->selectSingleNode( L"./D:getetag", &node2);
			resText = (LPCWSTR)NULL;
			result = node2->get_text( &resText );
			tempEtag = resText;

			// Get displayname
			result = node->selectSingleNode( L"./D:displayname", &node2);
			resText = (LPCWSTR)NULL;
			result = node2->get_text( &resText );
			tempName = resText;
			//UnEscapeText(tempName);
			namePairs->insert( pair<wstring,wstring>(tempName, tempEtag) );
		}
	} // if

    return result;
}

HRESULT CaldavConnection::ValidateFilename( wstring name )
{
    HRESULT result = S_OK;

    boost::wregex nameReg( L"^[a-zA-Z0-9!()',*$%.+_-]{1,}$");

    if( boost::regex_match(name, nameReg ) )
    {
        result = S_OK;
    }
    else
    {
        result = E_FAIL;
    }

    return result;
}

HRESULT CaldavConnection::GetEventsByName( list<wstring> names,
                           otlkcon_calendar_component_array **comps)
{
    HRESULT result = S_OK;
    wstring reqStr;
    list <wstring>::iterator namesIter;
    LONG status;
    CComBSTR resText;
    int validNameCount = 0;

	OTLKCON_ENTERCRITICALSECTION(&objectCS, "HTTP>CaldavConnection::GetEventsByName");
	MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;

	reqStr = L"<?xml version=\"1.0\"?>\
			  <C:calendar-multiget xmlns:C=\"urn:ietf:params:xml:ns:caldav\" xmlns:D=\"DAV:\">\
			  <D:prop>\
			  <D:getetag/>\
			  <D:displayname/>\
			  <C:calendar-data/>\
			  </D:prop>\n";
	for ( namesIter = names.begin( ); namesIter != names.end( ); namesIter++ )
	{
        // validate remote filename
        wstring tempStr = *namesIter;
        if( ValidateFilename( tempStr ) == S_OK )
        {
            validNameCount++;
		    reqStr += L"<D:href>";
		    reqStr += uri;
		    reqStr += *namesIter + L"</D:href>\n";
        }
        else
        {
            OTLKCON_DEBUGBREAK;
        }
	}
	reqStr += L"</C:calendar-multiget>";
	OutputDebugStringW( reqStr.c_str() );

	int  iAttempts = 2;

    // Send only if we have valid files to send.
	while(iAttempts-- && validNameCount)
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );

		result = pXMLHttpReq->open( L"REPORT",
			bstr_t(uri.c_str()), 
			variant_t(VARIANT_TRUE), 
			variant_t(user.c_str()), 
			variant_t(pass.c_str()) );

		result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/xml" );

		result = pXMLHttpReq->setRequestHeader( L"Depth", L"1") ;

		// FIXME: Needs a timeout or made aSync.
		result = pXMLHttpReq->send( variant_t(reqStr.c_str()) );

		if(SUCCEEDED(result))
		{
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				//releasing request
				pXMLHttpReq = NULL;
				//pXMLHttpReq->abort();
			} // if
			else
			{
				break;
			} // else
		} // if
	} // while

    if(SUCCEEDED(result) && pXMLHttpReq)
    {
		result = pXMLHttpReq->get_status( &status );
		if( status < 200 ||status > 299 )
		{
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( (LPCWSTR)resText );
			return E_FAIL;
		}

		resText = (LPCWSTR)NULL;
		result = pXMLHttpReq->get_responseText( &resText );
		OutputDebugStringW( (LPCWSTR)resText );

		MSXML2::IXMLDOMDocument2Ptr responseXML;
		result = pXMLHttpReq->get_responseXML( (IDispatch**)&responseXML );

		result = ReportResponseXML2Components( responseXML, comps);
    } // if
    return result;
}

HRESULT CaldavConnection::DeleteResource( wstring name )
{
    HRESULT result = S_OK;
    MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;
    int iAttempts = 2;

    name = uri + name;
	while(iAttempts--) 
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );

		result = pXMLHttpReq->open( L"DELETE",
            bstr_t(name.c_str()), 
			variant_t(VARIANT_TRUE),
			variant_t(user.c_str()), 
			variant_t(pass.c_str()) );

		result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/calendar" );

		result = pXMLHttpReq->send();
		if(SUCCEEDED(result))
		{
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				pXMLHttpReq = NULL;
				//pXMLHttpReq->abort();
			} // if
			else
			{
				break;
			} // else
		} // if
	} // while

    if(SUCCEEDED(result))
	{
		LONG status;
		CComBSTR resText = (LPCWSTR)NULL;

		result = pXMLHttpReq->get_responseText( &resText );
		OutputDebugStringW( (LPCWSTR)resText );

		result = pXMLHttpReq->get_status( &status );
		if( status != 200 && status != 201 && status != 204 && status != 207)
		{
			resText = (LPCWSTR)NULL;
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( (LPCWSTR)resText );
			return E_FAIL;
		}
	} // if

    return result;
}

HRESULT CaldavConnection::GetEventsByRange( 
                                 struct icaltimetype tm_start,
                                 struct icaltimetype tm_end,
                                 otlkcon_calendar_component_array **comps )
{
    HRESULT result = S_OK;
    LONG status;
    CComBSTR resText = (LPCWSTR)NULL;
    errno_t resErr;
    

	OTLKCON_ENTERCRITICALSECTION(&objectCS, "HTTP>CaldavConnection::GetEventsByRange");
	MSXML2::IXMLHTTPRequestPtr pXMLHttpReq;

	wchar_t reqstr[1023] = {0};
	resErr = _snwprintf_s(reqstr, 1023, 1022, 
		L"<?xml version=\"1.0\"?>\
		 <C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\
		 <D:prop xmlns:D=\"DAV:\">\
		 <D:getetag/>\
		 <D:displayname/>\
		 <C:calendar-data/>\
		 </D:prop>\
		 <C:filter>\
		 <C:comp-filter name=\"VCALENDAR\">\
		 <C:comp-filter name=\"VEVENT\">\
		 <C:time-range start=\"%S\" \
		 end=\"%S\"/>\
		 </C:comp-filter>\
		 </C:comp-filter>\
		 </C:filter>\
		 </C:calendar-query>", 
		 icaltime_as_ical_string(tm_start), icaltime_as_ical_string(tm_end));

	int  iAttempts = 2;
	while(iAttempts--) 
	{
		result = CoCreateInstance(
			CLSID_XMLHTTPRequest,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_IXMLHttpRequest,
			(LPVOID*)&(pXMLHttpReq) );

		result = pXMLHttpReq->open( L"REPORT",
			bstr_t(uri.c_str()), 
			variant_t(VARIANT_TRUE),
			variant_t(user.c_str()), 
			variant_t(pass.c_str()) );

		result = pXMLHttpReq->setRequestHeader( L"Content-Type", L"text/xml" );

		result = pXMLHttpReq->setRequestHeader( L"Depth", L"1" );

		result = pXMLHttpReq->send( variant_t(reqstr) );

		if(SUCCEEDED(result))
		{
			result = WaitForResult(pXMLHttpReq);
			if(FAILED(result))
			{
				pXMLHttpReq = NULL;
				//pXMLHttpReq->abort();
			} // if
			else
			{
				break;
			} // else
		} // if
	} // while
	
	if(SUCCEEDED(result))
	{
		result = pXMLHttpReq->get_status( &status );
		if( status != 200 && status != 207 )
		{
			result = pXMLHttpReq->get_statusText( &resText );
			OutputDebugStringW( resText );
			return E_FAIL;
		}
		resText = (LPCWSTR)NULL;
		result = pXMLHttpReq->get_responseText( &resText );
		OutputDebugStringW( resText );

		MSXML2::IXMLDOMDocument2Ptr responseXML;
		result = pXMLHttpReq->get_responseXML( (IDispatch**)&responseXML );

		result = ReportResponseXML2Components( responseXML, comps );
	} // if
    return result;
}

// FIXME: Use the event thread's message loop instead.
//        Consuming a lot of CPU time.
HRESULT CaldavConnection::WaitForResult(MSXML2::IXMLHTTPRequestPtr pRequest)
{
	LONG lReadyState = 0;
	HRESULT hResult = pRequest->get_readyState(&lReadyState);
	DWORD dwStart = GetTickCount();
	MSG msg;
    int count = 0;
//    errno_t resErr;
    std::wostringstream output;

	while(lReadyState != READYSTATE_COMPLETE)
	{
        output.clear();
        output << L"WaitForResult : Waiting.... "
               << ++count <<  L"\r\n";

        OutputDebugStringW( output.str().c_str() );
		if (PeekMessage(&msg, 0, 0 ,0, PM_REMOVE))
		{
			TranslateMessage (&msg) ;
			DispatchMessage (&msg) ;
		}
		hResult = pRequest->get_readyState(&lReadyState);
		if(FAILED(hResult))
		{
			break;
		} // if

        // TODO: Tuning sleep seems to help speed.  Might need to
        //       be a parameter.
        Sleep(OTLKCON_XP_CALDAV_SERVER_RESPONSE_PERIOD);

		if((GetTickCount() - dwStart) > 60000) // waiting for 60 seconds
		{
			break;
		} // if
	} // while
	if(SUCCEEDED(hResult) && lReadyState == READYSTATE_COMPLETE)
	{
		hResult = S_OK;
	} // if
	else
	{
		hResult = E_FAIL;
	} // else
	
	return hResult;
}

void CaldavConnection::EscapeText(wstring &strText)
{
}

void CaldavConnection::UnEscapeText(wstring &strText)
{
	wstring::size_type iPos = strText.find(L'%',0);
	while(wstring::npos != iPos) 
	{
		int iSym = 0;
		wchar_t szSymbol[3] = {0};
		szSymbol[0] = strText[iPos+1];
		szSymbol[1] = strText[iPos+2];
		swscanf_s(szSymbol, L"%02x", &iSym);
		wchar_t sym = (wchar_t)iSym;
		strText.replace(iPos,3, 1, sym);
		iPos = strText.find(L'%',0);
	} // while
}
